﻿using System;
using System.Collections.Generic;
using System.IO;
using Zjh.PackSerializer.Converters;
using Zjh.PackSerializer.Converters.Base;

namespace Zjh.PackSerializer
{
    public class PackSerializer : IPackSerializer
    {
        public List<IPackConverter> Converters { get; }

        public PackSerializer()
        {
            Converters = new List<IPackConverter>();
        }

        public T Deserialize<T>(byte[] buffer)
        {
            var obj = Deserialize(buffer, typeof(T));
            if (obj == null)
            {
                return default(T);
            }
            return (T)obj;
        }

        public object Deserialize(byte[] buffer, Type objectType)
        {
            using (MemoryStream ms = new MemoryStream(buffer))
            {
                return Deserialize(ms, objectType);
            }
        }

        public object Deserialize(Stream stream, Type objectType)
        {
            for (int i = Converters.Count - 1; i >= 0; i--)
            {
                var converter = Converters[i];
                if (converter.CanConvert(objectType))
                {
                    if (converter.Read(stream, objectType, this, out object value))
                    {
                        return value;
                    }
                }
            }
            return null;
        }

        public byte[] Serialize<T>(T obj)
        {
            return Serialize(typeof(T), obj);
        }

        public byte[] Serialize(Type objectType, object obj)
        {
            using (MemoryStream ms = new MemoryStream(4096))
            {
                Serialize(ms, objectType, obj);
                return ms.ToArray();
            }
        }

        public void Serialize(Stream stream, Type objectType, object obj)
        {
            for (int i = Converters.Count - 1; i >= 0; i--)
            {
                var converter = Converters[i];
                if (converter.CanConvert(objectType))
                {
                    if (converter.Write(stream, objectType, obj, this))
                    {
                        return;
                    }
                }
            }
        }
    }
}